"""
@File    :  mms_test_layout.py
@Author  :  LeeSen
@Date    :  2024-08-26
@Version :  Python 3.7.4
"""
from PyQt5.QtCore import QThread, pyqtSignal, QTimer
from PyQt5.QtWidgets import QVBoxLayout, QLabel, QLineEdit, QPushButton, QTextEdit, QMessageBox, QComboBox, \
    QGridLayout

from common_config_param import GlobalParam
from common_function import welcome_init, ip_format_check, clear_log, log_format_print, int_num_check, \
    debug_log_format_print
from mms_client import MmsClient


class MmsDepTestThread(QThread):
    message_signal = pyqtSignal(dict)

    def __init__(self, thread_id, config_data):
        super().__init__()
        self.stop_flag = False
        self.config_data = config_data
        self.thread_id = thread_id

    def run(self):
        self.message_code_dict("200", "Thread ID:" + str(
                                                   self.thread_id) + " IEC61850 MMS Dep Test Thread will Start", None)
        mms_client = MmsClient(self.config_data["device_ip"], self.config_data["release_method"])
        if self.config_data["keep_the_connection"]:
            connect_flag = mms_client.on_connect()
            if not connect_flag:
                self.stop_flag = True
                self.message_code_dict("404",
                                       "Thread ID:" + str(self.thread_id) + " has except.Please Check your config.1",
                                       None)
        index = 0
        while (not self.stop_flag) and (index < self.config_data["test_times"]):
            if not self.config_data["keep_the_connection"]:
                connect_flag = mms_client.on_connect()
                if not connect_flag:
                    self.stop_flag = True
                    self.message_code_dict("404",
                                           "Thread ID:" + str(self.thread_id) + " has except.Please Check your config.2",
                                           None)
                    break
            for data_item in self.config_data["hex_data"]:
                if len(data_item) != 0:
                    send_flag = mms_client.send_mms_package(data_item)
                    if not send_flag:
                        self.stop_flag = True
                        self.message_code_dict("404",
                                               "Thread ID:" + str(
                                                   self.thread_id) + " has except.Please Check your config.3",
                                               None)
                        break
            if (not self.config_data["keep_the_connection"]) and (not self.stop_flag):
                mms_client.dis_connect()
            index += 1
        if self.config_data["keep_the_connection"] and not self.stop_flag:
            mms_client.dis_connect()
        self.stop_flag = True

    def stop_thread_test(self):
        self.stop_flag = True
        self.message_code_dict("200", "Thread ID:" + str(
                                                   self.thread_id) + " IEC61850 MMS Dep Test Thread will Stop", None)

    def message_code_dict(self, code_id, message, data):
        res = {"code": code_id, "message": message, "data": data}
        self.message_signal.emit(res)

    def is_running(self):
        return not self.stop_flag


class MMSTestLayout(QVBoxLayout):

    def __init__(self):
        super().__init__()

        self.thread_dict = {}

        self.timer = QTimer(self)
        self.timer.start(3000)

        self.config_data = {"device_ip": "127.0.0.1",
                            "thread_num": 1,
                            "test_times": 1,
                            "keep_the_connection": True,
                            "release_method": True,
                            "hex_data": []
                            }

        ip_label = QLabel('DUT IP Address:')
        self.ip_input = QLineEdit()
        self.ip_input.setPlaceholderText("Enter DUT IP address")
        self.ip_input.setText("127.0.0.1")
        self.ip_input.editingFinished.connect(self.validate_ip)

        self.start_button = QPushButton('Start')
        self.start_button.clicked.connect(self.start_test)
        self.start_button.setEnabled(True)

        self.stop_button = QPushButton('Stop')
        self.stop_button.clicked.connect(self.stop_test)
        self.stop_button.setEnabled(False)

        clear_log_button = QPushButton("Clear Log")
        clear_log_button.clicked.connect(lambda x: clear_log(self.log_area))

        config_param_layout = QGridLayout()

        thread_num_label = QLabel("Thread Num:")
        self.thread_num_box = QComboBox()

        test_times_label = QLabel("Test Times:")
        self.test_times_input = QLineEdit()
        self.test_times_input.setText("1")

        keep_the_same_connection_label = QLabel("Keep The Same Connection:")
        self.keep_the_same_connection_box = QComboBox()
        self.keep_the_same_connection_box.addItem("Yes")
        self.keep_the_same_connection_box.addItem("No")

        release_method_label = QLabel("Release Method:")
        self.release_method_box = QComboBox()
        self.release_method_box.addItem("Normal")
        self.release_method_box.addItem("Abort")

        config_param_layout.addWidget(ip_label, 0, 0)
        config_param_layout.addWidget(self.ip_input, 0, 1)
        config_param_layout.addWidget(self.start_button, 0, 4)
        config_param_layout.addWidget(self.stop_button, 0, 5)
        config_param_layout.addWidget(clear_log_button, 0, 6)

        config_param_layout.addWidget(thread_num_label, 1, 0)
        config_param_layout.addWidget(self.thread_num_box, 1, 1)
        config_param_layout.addWidget(test_times_label, 2, 0)
        config_param_layout.addWidget(self.test_times_input, 2, 1)

        config_param_layout.addWidget(keep_the_same_connection_label, 1, 2)
        config_param_layout.addWidget(self.keep_the_same_connection_box, 1, 3)
        config_param_layout.addWidget(release_method_label, 2, 2)
        config_param_layout.addWidget(self.release_method_box, 2, 3)

        self.addLayout(config_param_layout)

        payload_data_layout = QVBoxLayout()

        payload_data_label = QLabel("Test Payload Data:")
        self.payload_data_area = QTextEdit()
        self.payload_data_area.setPlaceholderText("IEC 61850 Test Payload Data")

        payload_data_layout.addWidget(payload_data_label)
        payload_data_layout.addWidget(self.payload_data_area)

        self.addLayout(payload_data_layout)

        log_label = QLabel("Real-time Logs")
        self.log_area = QTextEdit()
        self.log_area.setPlaceholderText("Log Area")
        self.log_area.setReadOnly(True)

        self.addWidget(log_label)
        self.addWidget(self.log_area)

        welcome_init(self.log_area)
        self.init_thread_num()
        self.init_append_log_data()

    def init_thread_num(self):
        for i in range(1, 17):
            self.thread_num_box.addItem(str(i))

    def init_append_log_data(self):
        log_format_print(self.log_area, "直接将报文的十六进制串复制粘贴在IEC 61850 Test Payload Data即可")
        log_format_print(self.log_area, "示例:[下面是一个报文，且报文最后没有分号]")
        log_format_print(self.log_area, "0102030405060708090a")
        log_format_print(self.log_area, "如果需要发送多个报文,需要将每个报文的十六进制串使用英文分号分隔")
        log_format_print(self.log_area, "示例:[下面是四个报文，中间使用;进行分隔,且报文最后没有分号,不需要换行]")
        log_format_print(self.log_area, "010203040506070809;0a0b0c0d0e0f0102;03040506070809;0a0b0c0d0e0f010203")

    def validate_ip(self):
        if ip_format_check(self.ip_input.text()):
            QMessageBox.warning(self.parentWidget().parentWidget(),
                                "Invalid IP",
                                "The entered IP address is invalid.")

    def config_param_check(self):
        host_ip = self.ip_input.text()
        if ip_format_check(host_ip):
            QMessageBox.warning(self.parentWidget().parentWidget(),
                                "Invalid IP",
                                "The entered IP address is invalid.")
            return True
        test_times = self.test_times_input.text()
        if int_num_check(test_times) or int(test_times) < 1:
            QMessageBox.warning(self.parentWidget().parentWidget(),
                                "Invalid Test Times Param",
                                "The entered Test Times Param is invalid.")
            return True
        test_payload_data = self.payload_data_area.toPlainText()
        for char_item in test_payload_data:
            if char_item not in "0123456789ABCDEFabcdef;":
                QMessageBox.warning(self.parentWidget().parentWidget(),
                                    "Invalid Test Payload Data Param",
                                    "The entered Test Payload Data Param is invalid.")
                return True
        return False

    def update_config_data(self):
        self.config_data["device_ip"] = self.ip_input.text()
        self.config_data["thread_num"] = int(self.thread_num_box.currentText())
        self.config_data["test_times"] = int(self.test_times_input.text())
        self.config_data["keep_the_connection"] = True if self.keep_the_same_connection_box.currentText() == "Yes" else False
        self.config_data["release_method"] = True if self.release_method_box.currentText() == "Normal" else False

        hex_data = self.payload_data_area.toPlainText()
        if len(hex_data) != 0:
            self.config_data["hex_data"].clear()
            for item in hex_data.split(";"):
                self.config_data["hex_data"].append(item)

    def update_message_or_data(self, res):
        if res["code"] == "404":
            log_format_print(self.log_area, res["message"])
        elif res["code"] == "200":
            log_format_print(self.log_area, res["message"])
        elif res["code"] == "666":
            log_format_print(self.log_area, res["message"])

    def timeout_handel(self):

        count_flag = 0
        for key, value in self.thread_dict.items():
            if value.is_running():
                count_flag += 1
        if count_flag == 0:
            self.stop_test()
        log_format_print(self.log_area, "There are still " + str(count_flag) + " threads running in the thread pool")

    def start_test(self):
        if GlobalParam.global_running_flag:
            QMessageBox.warning(self.parentWidget().parentWidget(), "WARNING",
                                "Other programs are running, please stop first")
            return
        if self.config_param_check():
            return
        self.update_config_data()
        for i in range(self.config_data["thread_num"]):
            thread_item = MmsDepTestThread(i+1, self.config_data)
            thread_item.message_signal.connect(self.update_message_or_data)
            self.thread_dict[i] = thread_item
        for key, values in self.thread_dict.items():
            values.start()
        self.start_button.setEnabled(False)
        self.stop_button.setEnabled(True)
        self.timer.timeout.connect(self.timeout_handel)
        GlobalParam.global_running_flag = True

    def stop_test(self):
        for key, values in self.thread_dict.items():
            if values.is_running():
                values.stop_thread_test()
        for i in range(self.config_data["thread_num"]):
            self.thread_dict.pop(i)
        self.start_button.setEnabled(True)
        self.stop_button.setEnabled(False)
        self.timer.timeout.disconnect()
        GlobalParam.global_running_flag = False

